package com.guigu.gulicart.service.impl;

import com.guigu.gulicart.entity.dto.AddToCartDto;
import com.guigu.gulicart.entity.dto.UpdateCartItemDto;
import com.guigu.gulicart.feign.ProductServer;
import com.guigu.gulicart.service.CartService;
import com.guigu.gulicommon.constant.enumes.cart.IsCheck;
import com.guigu.gulicommon.context.UserContext;
import com.guigu.gulicommon.entity.dto.SelectSkuInfoPagesDto;
import com.guigu.gulicommon.entity.pojo.Cart;
import com.guigu.gulicommon.entity.pojo.CartItem;
import com.guigu.gulicommon.entity.pojo.res.Res;
import com.guigu.gulicommon.entity.vo.SkuInfoPageVo;
import com.guigu.gulicommon.exception.BaseException;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.guigu.gulicommon.constant.RedisKey.CART_PREFIX;
import static com.guigu.gulicommon.constant.enumes.res.AddResCode.INSERT_PRODUCT_TO_CART_SUCCESS;
import static com.guigu.gulicommon.constant.enumes.res.DeleteResCode.DELETE_PRODUCT_SUCCESS;
import static com.guigu.gulicommon.constant.enumes.res.SelectResCode.SELECT_CART_INFO_SUCCESS;
import static com.guigu.gulicommon.constant.enumes.res.ServerResCode.UNKNOWN_EXCEPTION;
import static com.guigu.gulicommon.constant.enumes.res.UpdateResCode.UPDATE_CART_ITEM_SUCCESS;

/**
 * @author 陈俊杰
 * @version 1.0
 * @since 2024/9/21 12:26
 */
@Service
@Log4j2
public class CartServiceImpl implements CartService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private ProductServer productServer;

    /**
     * 添加商品
     *
     * @param addToCartDto 添加商品参数
     * @return 添加结果
     */
    @Override
    public Res<CartItem> addToCart(AddToCartDto addToCartDto) {
        //1 获取要操作的购物车
        BoundHashOperations<String, Object, Object> cartOperations = this.getCartOperations();
        CartItem cartItem = new CartItem();
        CartItem cart = (CartItem) cartOperations.get(addToCartDto.getSkuId().toString());
        if (cart != null && StringUtils.isNotBlank(cart.toString())) {
            //2 更新数量
            BeanUtils.copyProperties(cart, cartItem);

            cartItem.setCount(cart.getCount() + addToCartDto.getNum());
            //3 更新到redis
            cartOperations.put(addToCartDto.getSkuId(), cartItem);
            return new Res<>(INSERT_PRODUCT_TO_CART_SUCCESS, cartItem);
        } else {
            //2 远程查询需要添加商品信息
            CompletableFuture<Void> getSkuInfoFuture = CompletableFuture.runAsync(() -> {
                SelectSkuInfoPagesDto selectSkuInfoPagesDto = new SelectSkuInfoPagesDto().setQueryParam(addToCartDto.getSkuId().toString());
                productServer.findPages(selectSkuInfoPagesDto);
                SkuInfoPageVo skuInfoPageVo = productServer.findPages(selectSkuInfoPagesDto).getData().get(0);
                cartItem.setIsCheck(IsCheck.CHECKED)
                        .setTitle(skuInfoPageVo.getSkuTitle())
                        .setImage(skuInfoPageVo.getSkuDefaultImg())
                        .setPrice(skuInfoPageVo.getPrice())
                        .setSkuId(skuInfoPageVo.getSkuId())
                        .setCount(addToCartDto.getNum());
            }, threadPoolTaskExecutor);
            //2.2 远程查询sku组合信息
            CompletableFuture<Void> getSkuAttrsFuture = CompletableFuture.runAsync(() -> {
                List<String> attrs = productServer.getAttrsListBySkuId(addToCartDto.getSkuId()).getData();
                cartItem.setSkuAttrs(attrs);

            }, threadPoolTaskExecutor);

            //2.3 等待远程调用完成
            try {
                CompletableFuture.allOf(getSkuInfoFuture, getSkuAttrsFuture).get();
            } catch (InterruptedException | ExecutionException e) {
                log.error("远程调用异步任务出现异常:[{}]", e.getMessage());
                throw new BaseException(UNKNOWN_EXCEPTION);
            }
            //3 放入redis
            cartOperations.put(addToCartDto.getSkuId(), cartItem);
        }


        return new Res<>(INSERT_PRODUCT_TO_CART_SUCCESS, cartItem);
    }

    /**
     * 获取购物车信息
     */
    @Override
    public Res<Cart> getCartInfo() {
        List<Object> values = this.getCartOperations().values();
        Cart cart = new Cart();

        if (values != null) {
            List<CartItem> cartItems = values.stream().map(v -> (CartItem) v).collect(Collectors.toList());
            cart.setCartItems(cartItems);
        }

        return new Res<>(SELECT_CART_INFO_SUCCESS, cart);
    }

    /**
     * 批量删除购物项
     *
     * @param ids 批量删除购物项的id集合
     * @return vo 被删除的购物项的信息
     */
    @Override
    public Res<Long> deleteByIdsBatch(List<Long> ids) {
//        List<Long> idsList = Arrays.asList(ids);
        this.getCartOperations().delete(ids);
        return new Res<>(DELETE_PRODUCT_SUCCESS, ids);
    }

    /**
     * 修改购物车项商品数量(通用)
     *
     * @param dto 修改购物车项商品数量的参数
     * @return vo 修改后购物车项商品数量的信息
     */
    @Override
    public Res<CartItem> update(UpdateCartItemDto dto) {
        CartItem cartItem = (CartItem) this.getCartOperations().get(dto.getSkuId());
        if (cartItem != null) {
            cartItem.setCount(dto.getCount());
            this.getCartOperations().put(dto.getSkuId(), cartItem);
        }
        return new Res<>(UPDATE_CART_ITEM_SUCCESS, cartItem);
    }

    /**
     * 从Redis中获取要操作的购物车
     *
     * @return 购物车
     */
    private BoundHashOperations<String, Object, Object> getCartOperations() {
        //1 从线程中获取用户Id
        Long userId = UserContext.getInstance().getCurrentUser().getId();
        //2 获取购物车
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(CART_PREFIX + userId);
        boundHashOperations.expire(30, TimeUnit.DAYS);
        return boundHashOperations;
    }
}
